﻿namespace DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.ServiceModel.DomainServices.Server;

    internal class CustomMethodProxyGenerator : ProxyGenerator
    {
        private ICollection<DomainServiceDescription> _domainServiceDescriptions;
        private Type _entityType;
        private NotificationMethodGenerator _notificationMethodGen;
        private CodeTypeDeclaration _proxyClass;

        public CustomMethodProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration proxyClass, Type entityType, ICollection<DomainServiceDescription> domainServiceDescriptions, NotificationMethodGenerator notificationMethodGen) : base(proxyGenerator)
        {
            this._entityType = entityType;
            this._proxyClass = proxyClass;
            this._domainServiceDescriptions = domainServiceDescriptions;
            this._notificationMethodGen = notificationMethodGen;
        }

        public override void Generate()
        {
            System.Func<CodeTypeMember, bool> func = null;
            string methodName;
            Dictionary<string, DomainOperationEntry> dictionary = new Dictionary<string, DomainOperationEntry>();
            Dictionary<string, DomainServiceDescription> dictionary2 = new Dictionary<string, DomainServiceDescription>();
            bool isDerivedEntityType = false;
            foreach (DomainServiceDescription description in this._domainServiceDescriptions)
            {
                Type rootEntityType = description.GetRootEntityType(this._entityType);
                isDerivedEntityType |= (rootEntityType != null) && (rootEntityType != this._entityType);
                foreach (DomainOperationEntry entry in description.GetCustomMethods(this._entityType))
                {
                    methodName = entry.Name;
                    if (dictionary.ContainsKey(methodName))
                    {
                        base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_DuplicateCustomMethodName, new object[] { methodName, this._entityType, dictionary2[methodName].DomainServiceType, description.DomainServiceType }));
                    }
                    else
                    {
                        if (func == null)
                        {
                            func = member => member.Name == methodName;
                        }
                        if (Enumerable.Any<CodeTypeMember>(this._proxyClass.Members.Cast<CodeTypeMember>(), func))
                        {
                            base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_NamingCollision_EntityCustomMethodNameAlreadyExists, new object[] { this._entityType, methodName }));
                            continue;
                        }
                        dictionary.Add(methodName, entry);
                        dictionary2.Add(methodName, description);
                    }
                }
            }
            foreach (KeyValuePair<string, DomainOperationEntry> pair in from entry in dictionary
                orderby entry.Key
                select entry)
            {
                this.GenerateCustomMethod(pair.Key, pair.Value);
                this.GenerateGuardProperty(pair.Key);
            }
            if (dictionary.Count > 0)
            {
                this.GenerateOnActionStateChanged(from key in dictionary.Keys
                    orderby key
                    select key, isDerivedEntityType);
            }
        }

        private void GenerateCustomMethod(string customMethodName, DomainOperationEntry customMethod)
        {
            base.ClientProxyGenerator.LogMessage(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_Generating_InvokeMethod, new object[] { customMethodName }));
            CodeMemberMethod method = new CodeMemberMethod {
                Name = customMethodName,
                Attributes = MemberAttributes.Public | MemberAttributes.Final
            };
            DomainOperationParameter[] parameterArray = customMethod.Parameters.ToArray<DomainOperationParameter>();
            string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_Custom_Method_Summary_Comment, new object[] { customMethodName });
            method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            for (int i = 1; i < parameterArray.Length; i++)
            {
                comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_Custom_Method_Parameter_Comment, new object[] { parameterArray[i].Name });
                method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(parameterArray[i].Name, comment, base.ClientProxyGenerator.IsCSharp));
            }
            IEnumerable<Attribute> attributes = customMethod.Attributes.Cast<Attribute>();
            CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_ThrewException_CodeMethod, new object[] { ex.Message, method.Name, this._proxyClass.Name, ex.InnerException.Message }), attributes, method.CustomAttributes, method.Comments);
            List<CodeExpression> list = new List<CodeExpression> {
                new CodePrimitiveExpression(customMethodName)
            };
            for (int j = 1; j < parameterArray.Length; j++)
            {
                DomainOperationParameter parameter = parameterArray[j];
                CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(parameter.ParameterType), base.ClientProxyGenerator, this._proxyClass), parameter.Name);
                IEnumerable<Attribute> enumerable2 = parameter.Attributes.Cast<Attribute>();
                string customCommentHeader = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_Parameter_FailedToGenerate, new object[] { parameter.Name });
                CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, this._proxyClass, ex => string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_ThrewException_CodeMethodParameter, new object[] { ex.Message, paramDecl.Name, method.Name, this._proxyClass.Name, ex.InnerException.Message }), enumerable2, paramDecl.CustomAttributes, method.Comments, customCommentHeader);
                method.Parameters.Add(paramDecl);
                list.Add(new CodeVariableReferenceExpression(parameter.Name));
            }
            string baseMethodName = customMethodName + "Invoking";
            string str4 = customMethodName + "Invoked";
            this._notificationMethodGen.AddMethodFor(baseMethodName, method.Parameters, null);
            this._notificationMethodGen.AddMethodFor(str4, null);
            method.Statements.Add(this._notificationMethodGen.GetMethodInvokeExpressionStatementFor(baseMethodName));
            method.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "InvokeAction", list.ToArray())));
            method.Statements.Add(this._notificationMethodGen.GetMethodInvokeExpressionStatementFor(str4));
            this._proxyClass.Members.Add(method);
            CodeMemberProperty property = new CodeMemberProperty {
                Attributes = MemberAttributes.Public | MemberAttributes.Final,
                HasGet = true,
                HasSet = false,
                Type = new CodeTypeReference(typeof(bool))
            };
            property.GetStatements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "IsActionInvoked", new CodeExpression[] { new CodeArgumentReferenceExpression("\"" + customMethodName + "\"") })));
            property.Name = "Is" + str4;
            comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_IsInvoked_Property_Summary_Comment, new object[] { customMethodName });
            property.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            CodeAttributeDeclaration declaration = CodeGenUtilities.CreateDisplayAttributeDeclaration(base.ClientProxyGenerator, this._proxyClass);
            property.CustomAttributes.Add(declaration);
            this._proxyClass.Members.Add(property);
        }

        private void GenerateGuardProperty(string customMethodName)
        {
            string str = string.Format(CultureInfo.InvariantCulture, "Can{0}", new object[] { customMethodName });
            base.ClientProxyGenerator.LogMessage(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_Generating_GuardProperty, new object[] { str }));
            CodeMemberProperty property = new CodeMemberProperty {
                Name = str,
                Type = CodeGenUtilities.GetTypeReference(typeof(bool), base.ClientProxyGenerator, this._proxyClass),
                Attributes = MemberAttributes.Public | MemberAttributes.Final
            };
            string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_CanInvoke_Property_Summary_Comment, new object[] { customMethodName });
            property.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            CodeAttributeDeclaration declaration = CodeGenUtilities.CreateDisplayAttributeDeclaration(base.ClientProxyGenerator, this._proxyClass);
            property.CustomAttributes.Add(declaration);
            property.GetStatements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "CanInvokeAction", new CodeExpression[] { new CodePrimitiveExpression(customMethodName) })));
            this._proxyClass.Members.Add(property);
        }

        private void GenerateOnActionStateChanged(IEnumerable<string> customMethodNames, bool isDerivedEntityType)
        {
            base.ClientProxyGenerator.LogMessage(DomainServices.Tools.Resource.EntityCodeGen_Generating_GuardUpdateMethod);
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "OnActionStateChanged",
                Attributes = MemberAttributes.Family | MemberAttributes.Override
            };
            if (isDerivedEntityType)
            {
                method.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), method.Name, new CodeExpression[0])));
            }
            foreach (string str in customMethodNames)
            {
                method.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), "UpdateActionState", new CodeExpression[] { new CodePrimitiveExpression(str), new CodePrimitiveExpression(string.Format(CultureInfo.InvariantCulture, "Can{0}", new object[] { str })), new CodePrimitiveExpression(string.Format(CultureInfo.InvariantCulture, "Is{0}Invoked", new object[] { str })) })));
            }
            this._proxyClass.Members.Add(method);
        }
    }
}

